package com.example.mycamerademo.cameraxlearing

import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.graphics.ImageFormat
import android.graphics.Matrix
import android.graphics.Point
import android.hardware.Camera.ACTION_NEW_PICTURE
import android.media.ImageReader
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.util.Size
import android.view.MotionEvent
import android.view.OrientationEventListener
import android.view.Surface
import android.webkit.MimeTypeMap
import android.widget.Toast
import androidx.camera.core.*
import androidx.camera.core.ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY
import androidx.camera.core.ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY
import androidx.camera.core.impl.LensFacingCameraFilter
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.CameraController
import androidx.camera.view.LifecycleCameraController
import androidx.camera.view.PreviewView
import androidx.camera.view.video.OutputFileOptions
import androidx.core.content.ContextCompat
import androidx.core.util.Consumer
import androidx.lifecycle.LifecycleOwner
import com.bumptech.glide.Glide
import com.example.mycamerademo.BaseViewBindActivity
import com.example.mycamerademo.R
import com.example.mycamerademo.cameraxlearing.view.CameraXCustomPreviewView
import com.example.mycamerademo.databinding.Camera2ActivityMainBinding
import com.example.mycamerademo.utils.ImageDataUtils
import com.google.common.util.concurrent.ListenableFuture
import java.io.*
import java.nio.ByteBuffer
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.*


class CameraXMainActivity : BaseViewBindActivity<Camera2ActivityMainBinding>() {

    private val TAG = "Camera2MainActivity"

    private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
    private lateinit var mPreviewView: PreviewView
    private lateinit var faceView: FaceView

    //图像预览配置
    private lateinit var preview: Preview

    //图像各种配置集合
    private lateinit var useCaseGroup: UseCaseGroup

    //摄像头前后选择
    private lateinit var cameraSelector: CameraSelector

    //图像分析
    private lateinit var imageAnalysis: ImageAnalysis

    //图像捕捉配置
    private lateinit var imageCapture: ImageCapture

    private lateinit var camera: Camera//相机对象

    //拍照，回调方法将在其中运行的执行器。
    val mTakePictureService: ExecutorService =
        ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, LinkedBlockingDeque(20))

    //拍照输出位置
    private lateinit var takePictureOut: ImageCapture.OutputFileOptions

    //照片存储位置
    private lateinit var savePicturePath: String


    override fun getLayoutInflateId(): Int {
        return R.layout.camera2_activity_main
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        cameraProviderFuture = ProcessCameraProvider.getInstance(this)

//        rotationSetting()
        cameraProviderFuture.addListener(Runnable {
            val cameraProvider = cameraProviderFuture.get()
            bindPreview(cameraProvider)

            //为什么放在回调里面因为防止初始化相机没成功 后续需要用到相机的东西
            takePicture()
            rotationCamera()
            //自定义手势
            initCameraListener()

        }, ContextCompat.getMainExecutor(this))


    }


    override fun initData() {

    }

    override fun initUI() {

    }

    /**
     * @implNote In checkPreviewUseCase, ImageReader will provide a Surface for camera preview test.
     *  When each ImageProxy is acquired, the AtomicInteger will be incremented.
     *  By doing so we can ensure the camera binding is working as expected.
     */
    private val reader = ImageReader.newInstance(1080, 1080, ImageFormat.YUV_420_888, 30)

    private fun bindPreview(cameraProvider: ProcessCameraProvider) {
        //预览设置
        preview = Preview.Builder()
            .setTargetRotation(windowManager.defaultDisplay.rotation)//预览角度,预览角度跟随设备走
            .setTargetAspectRatio(AspectRatio.RATIO_16_9)//宽高比，与下面setTargetResolution方法冲突
//            .setTargetResolution(Size(reader.width, reader.height))
            .build()
        //选择相机
        cameraSelector = CameraSelector.Builder()
            .requireLensFacing(CameraSelector.LENS_FACING_BACK)
            .build()
//        不清楚效果
//        val executor = Executors.newSingleThreadExecutor()
//        preview.setSurfaceProvider( executor,
//            Preview.SurfaceProvider { request: SurfaceRequest ->
//                val surface = reader.surface
//                Log.i("tgwCameraPreviewTest", String.format("providing: %s", surface))
//                request.provideSurface(surface, executor!!, Consumer<SurfaceRequest.Result> {result->
//                    when (result.resultCode) {
//                        SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY -> {
//                            Log.i("tgwCameraPreviewTest", result.toString())
//                        }
//                        SurfaceRequest.Result.RESULT_REQUEST_CANCELLED, SurfaceRequest.Result.RESULT_INVALID_SURFACE, SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED, SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE -> {
//                            Log.e("tgwCameraPreviewTest", result.toString())
//                        }
//                    }
//                })
//            })


        // 图片分析
        imageAnalysis = ImageAnalysis.Builder()
            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
            .setImageQueueDepth(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
            .setTargetRotation(Surface.ROTATION_90)
            .build()



        preview.setSurfaceProvider(mBind.preview.surfaceProvider)


        val useCase: Array<UseCase> = arrayOf<UseCase>(preview, imageAnalysis)
        useCaseGroup = UseCaseGroup.Builder()
            .addUseCase(preview)//图像预览
            .addUseCase(imageAnalysis)
            .addUseCase(rotationImageSetting()) //图像捕捉
            .build()
        camera =
            cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, useCaseGroup)

        //设置，闪光灯聚焦缩放等,必须在camera后面
//        setPreviewControl()
    }

    private fun setFaceInfoListener(previewView: PreviewView, imageProxy: ImageProxy): Matrix {
        val cropRect = imageProxy.cropRect
        mBind.faceView.mFaceRect = cropRect
        Log.d("tgwTAG", "setFaceInfoListener: ${cropRect.toString()}")
        val rotationDegrees = imageProxy.imageInfo.rotationDegrees
        val matrix = Matrix()

        // A float array of the source vertices (crop rect) in clockwise order.
        val source = floatArrayOf(
            cropRect.left.toFloat(),
            cropRect.top.toFloat(),
            cropRect.right.toFloat(),
            cropRect.top.toFloat(),
            cropRect.right.toFloat(),
            cropRect.bottom.toFloat(),
            cropRect.left.toFloat(),
            cropRect.bottom.toFloat()
        )

        // A float array of the destination vertices in clockwise order.
        val destination = floatArrayOf(
            0f,
            0f,
            previewView.width.toFloat(),
            0f,
            previewView.width.toFloat(),
            previewView.height.toFloat(),
            0f,
            previewView.height.toFloat()
        )

        // The destination vertexes need to be shifted based on rotation degrees. The
        // rotation degree represents the clockwise rotation needed to correct the image.

        // Each vertex is represented by 2 float numbers in the vertices array.
        val vertexSize = 2
        // The destination needs to be shifted 1 vertex for every 90° rotation.
        val shiftOffset = rotationDegrees / 90 * vertexSize;
        val tempArray = destination.clone()
        for (toIndex in source.indices) {
            val fromIndex = (toIndex + shiftOffset) % source.size
            destination[toIndex] = tempArray[fromIndex]
        }
        matrix.setPolyToPoly(source, 0, destination, 0, 4)
        return matrix
    }

    /**旋转相机，预览跟随*/
    private fun rotationImageSetting(): ImageCapture {
        imageCapture = ImageCapture.Builder()
            .setCaptureMode(CAPTURE_MODE_MINIMIZE_LATENCY)//缩短照片拍摄的延迟时间
            .setCaptureMode(CAPTURE_MODE_MAXIMIZE_QUALITY)//优化照片质量
            .setFlashMode(ImageCapture.FLASH_MODE_ON)//闪光灯模式
            .build()
        imageCapture.targetRotation = windowManager.defaultDisplay.rotation//成像角度跟随设备走
        val orientationEventListener = object : OrientationEventListener(this as Context) {
            override fun onOrientationChanged(orientation: Int) {
                Log.d(TAG, "onOrientationChanged: 旋转角度：$orientation")
                // Monitors orientation values to determine the target rotation value
                val rotation: Int = when (orientation) {
                    in 45..134 -> {
                        //预览界面跟随，设备旋转
                        preview.targetRotation = Surface.ROTATION_270

                        Surface.ROTATION_270
                    }
                    in 135..224 -> {
                        preview.targetRotation = Surface.ROTATION_180

                        Surface.ROTATION_180
                    }
                    in 225..314 -> {
                        preview.targetRotation = Surface.ROTATION_90

                        Surface.ROTATION_90
                    }
                    else -> {
                        preview.targetRotation = Surface.ROTATION_0

                        Surface.ROTATION_0
                    }
                }
                //设置输出图像的所需旋转
                imageCapture.targetRotation = rotation
            }
        }
        //启用OrientationEventListener，当设备方向改变时，它将监视传感器并调用onOrientationChanged
        orientationEventListener.enable()
        //拍照
        mTakePictureService.execute {
            //回调方法将在其中运行的执行器
            Log.d(TAG, "拍照execute：")
        }

        return imageCapture
    }


    /**前后相机转换*/
    @SuppressLint("RestrictedApi")
    private fun rotationCamera() {
        mBind.rotation.setOnClickListener {
            for (filter in cameraSelector.cameraFilterSet) {
                if (filter is LensFacingCameraFilter) {
                    val newLensFacing = filter.lensFacing
                    if (newLensFacing == CameraSelector.LENS_FACING_FRONT && CameraUtils.hasBackFacingCamera()) {
//                        if (cameraSelector == CameraSelector.DEFAULT_BACK_CAMERA) //默认摄像头
                        Toast.makeText(this, "没有后置摄像头", Toast.LENGTH_SHORT).show()
                        cameraSelector = CameraSelector.Builder()
                            .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                            .build()
                    } else {
                        if (CameraUtils.hasFrontCamera(this@CameraXMainActivity)) {
                            Toast.makeText(this, "没有前置摄像头", Toast.LENGTH_SHORT).show()
                            cameraSelector = CameraSelector.Builder()
                                .requireLensFacing(CameraSelector.LENS_FACING_FRONT)
                                .build()
                        }
                    }
                    //重新绑定相机的配置,需要先解绑
                    cameraProviderFuture.get().unbindAll()
                    preview.setSurfaceProvider(mBind.preview.surfaceProvider)
                    cameraProviderFuture.get()
                        .bindToLifecycle(this as LifecycleOwner, cameraSelector, useCaseGroup)
                }
            }
        }
    }

    /**
     *拍照
     */
    private fun takePicture() {

        savePicturePath =
            "${this@CameraXMainActivity.getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.path}/CameraX"
        val filePath = File(savePicturePath)
        if (!filePath.exists()) {
            filePath.mkdirs()
        }

        val mService: ExecutorService =
            ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, LinkedBlockingDeque(20))



        mBind.takePicture.setOnClickListener {//此方法为拍摄的图片提供内存缓冲区

            //点击拍照分析图像，正常应该用一个线程不停检测，分析图像
            imageAnalysis.setAnalyzer(mService, { imageProxy ->
                Log.d(TAG, "setAnalyzer 图像格式: ${imageProxy.format}对应ImageFormat.YUV_420_888:${ImageFormat.YUV_420_888}")
                //这里拿到字节数组可以为 所 欲 为 ！
                val buffer = imageProxy.planes[0].buffer
                val data = ByteArray(buffer.remaining())
//                val data = ByteArray(buffer.capacity())
                buffer.get(data)
                Log.d(TAG, "setAnalyzer: ${buffer.toString()}")
              val imageBitmap=  ImageDataUtils.bytesToBitmap(data,imageProxy.width,imageProxy.height)
                saveBytesToFile(
                    "/storage/emulated/0/Android/data/com.example.mycamerademo/files/Pictures/CameraX/tgwaa.jpg",
                    data
                )
                runOnUiThread {
                    Glide.with(this@CameraXMainActivity).load(imageBitmap)
                        .into(mBind.ivDisplay)
                }
                Log.d(TAG, "bindPrevie 字节: ${data.toString()}")
                imageProxy.close()
//                    setFaceInfoListener(mPreviewView, imageProxy)
            })


            Log.d(TAG, "拍照点击")
            imageCapture.takePicture(
                mTakePictureService,
                object : ImageCapture.OnImageCapturedCallback() {
                    override fun onCaptureSuccess(image: ImageProxy) {
                        super.onCaptureSuccess(image)
                        Log.d(TAG, "拍照onCaptureSuccess：${image.imageInfo}")
                    }

                    override fun onError(exception: ImageCaptureException) {
                        super.onError(exception)
                        Log.d(
                            TAG,
                            "拍照onError：${exception.imageCaptureError}---message:${exception.message}"
                        )
                    }
                })
            //照片文件输出位置
            val pictureFile = File(savePicturePath + "/${System.currentTimeMillis()}.jpg)")
            val outputOptions =
                ImageCapture.OutputFileOptions.Builder(pictureFile).build()

            imageCapture.takePicture(//此方法将拍摄的图片保存到提供的文件位置。
                outputOptions,
                mTakePictureService,
                object : ImageCapture.OnImageSavedCallback {
                    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                        Log.d(
                            TAG,
                            "拍照保存onImageSaved 不知道为什么outputFileResults的uri会为null：${outputFileResults}"
                        )
                        val savedUri = Uri.fromFile(pictureFile)
                        Log.d(TAG, "拍照保存Photo capture succeeded 原路径: $pictureFile")
                        Log.d(TAG, "拍照保存Photo capture succeeded uri路径: $savedUri")
//                        runOnUiThread {
//                            Glide.with(this@CameraXMainActivity).load(savedUri)
//                                .into(mBind.ivDisplay)
//                        }
                        onFileSaved(savedUri)
                    }

                    override fun onError(exception: ImageCaptureException) {
                        Log.d(
                            TAG,
                            "拍照保存onError：${exception.imageCaptureError}---message:${exception.message}"
                        )
                    }
                })
        }
    }

    /**通知新加了图片*/
    private fun onFileSaved(savedUri: Uri) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            sendBroadcast(Intent(android.hardware.Camera.ACTION_NEW_PICTURE, savedUri))
        }
        val mimeTypeFromExtension: String? = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
            MimeTypeMap
                .getFileExtensionFromUrl(savedUri.path)
        )
        MediaScannerConnection.scanFile(
            applicationContext,
            arrayOf(File(savedUri.path).absolutePath),
            arrayOf(mimeTypeFromExtension)
        ) { path, uri ->
            Log.d(TAG, "拍照保存Image capture scanned into media store: \$uri$uri")
        }

    }


    private fun imageAnalysisSetting() {
        val imageAnalysis = ImageAnalysis.Builder()
            .setTargetResolution(Size(1080, 1920))
            .build()
    }

    /**
     * 初始化手势动作
     * 在preview上盖了一层进行手势监听，根据手势做不同效果
     * 目前xml中view隐藏
     */
    private fun initCameraListener() {
        val zoomState = camera.cameraInfo.zoomState
        mBind.customView.setCustomTouchListener(object :
            CameraXCustomPreviewView.CustomTouchListener {
            override fun zoom(delta: Float) {
                //双指缩放
                zoomState.value?.let {
                    val currentZoomRatio = it.zoomRatio
                    camera.cameraControl.setZoomRatio(currentZoomRatio * delta)
                }
            }

            override fun click(x: Float, y: Float) {
                //点击对焦
                setUpTapToFocus(x, y)
            }

            override fun doubleClick(x: Float, y: Float) {
                //双击放大缩小
                zoomState.value?.let {
                    val currentZoomRatio = it.zoomRatio
                    if (currentZoomRatio > it.minZoomRatio) {
                        camera!!.cameraControl.setLinearZoom(0f)
                    } else {
                        camera!!.cameraControl.setLinearZoom(0.5f)
                    }
                }
            }

            override fun longClick(x: Float, y: Float) {

            }
        })
    }

    /**
     *仿照源码
     * 见类CameraController中的onTapToFocus方法
     *
     * ：https://developer.android.google.cn/reference/kotlin/androidx/camera/core/CameraControl?hl=en#setlinearzoom
     */
    private fun setUpTapToFocus(x: Float, y: Float) {
        val AF_SIZE = 1.0f / 6.0f
        val AE_SIZE = AF_SIZE * 1.5f
        val afPoint: MeteringPoint =
            mBind.preview.meteringPointFactory.createPoint(x, y, AF_SIZE)
        val aePoint: MeteringPoint =
            mBind.preview.meteringPointFactory.createPoint(x, y, AE_SIZE)
        val focusMeteringAction =
            FocusMeteringAction.Builder(afPoint, FocusMeteringAction.FLAG_AF)
                .addPoint(aePoint, FocusMeteringAction.FLAG_AE)
                .build()

        camera.cameraControl.startFocusAndMetering(focusMeteringAction)?.let {
            it.addListener({
                try {
                    val result = it.get() as FocusMeteringResult
                    if (result.isFocusSuccessful) {
                        Log.d(TAG, "setUpTapToFocus: isSucceed")
                    } else {
                        Log.d(TAG, "setUpTapToFocus: failed")
                    }
                } catch (e: Exception) {
                    Log.d(TAG, "setUpTapToFocus: is Exception ${e.message}")
                }
            }, ContextCompat.getMainExecutor(this))
        }


    }

    fun saveBytesToFile(filePath: String?, data: ByteArray?) {
        val file = File(filePath)
        var outStream: BufferedOutputStream? = null
        try {
            outStream = BufferedOutputStream(FileOutputStream(file))
            outStream.write(data)
            outStream.flush()
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            if (null != outStream) {
                try {
                    outStream.close()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
        }
    }

    /**不太熟 有坑*/
    private fun setPreviewControl() {
        val controll = LifecycleCameraController(this).apply {
            bindToLifecycle(this@CameraXMainActivity)
        }
        mBind.preview.controller = controll
    }
}